package ru.spbau.lifeincube.logic;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Montsev Mikhail
 *         Date: 12.05.13
 *         Time: 19:04
 */
public class Game {
    private Field mField;
    private int liveLeft;
    private int liveRight;
    private int liveNeed;
    private StateStorage mStateStorage;

    public Game(Field field, int liveLeft, int liveRight, int liveNeed) {
        this.mField = field;
        this.liveLeft = liveLeft;
        this.liveRight = liveRight;
        this.liveNeed = liveNeed;
    }

    public boolean makeMove() {
        ArrayList<Cell> changes = getListForChange();
        if (changes.size() != 0) {
            mStateStorage.nextState(getAddedCells(changes), getRemovedCells(changes));
            for (Cell change : changes)
                mField.changeCellState(change);
        } else return false;
        System.out.println("added: " + addedCells().size());
        System.out.println("removed: " + removedCells().size());
        return true;
    }

    public List<Cell> addedCells() {
        return mStateStorage.getAddedCells();
    }

    private List<Cell> getAddedCells(List<Cell> changeList) {
        List<Cell> list = new ArrayList<>();
        for (Cell cell : changeList)
            if (cell != null) {
                if (!cell.getState())
                    list.add(cell);
            }
        return list;
    }

    public List<Cell> removedCells() {
        return mStateStorage.getRemovedCells();
    }

    private List<Cell> getRemovedCells(List<Cell> changeList) {
        List<Cell> list = new ArrayList<>();
        for (Cell cell : changeList)
            if (cell != null)
                if (cell.getState())
                    list.add(cell);
        return list;
    }

    private ArrayList<Cell> getListForChange() {
        final ArrayList<Cell> list = new ArrayList<>();
        Thread[] threads = new Thread[mField.getSizeX()];
        for (int i = 0; i < mField.getSizeX(); i++) {
            final int counter = i;
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < mField.getSizeY(); j++) {
                        for (int k = 0; k < mField.getSizeZ(); k++) {
                            Cell cell = mField.getCell(counter, j, k);
                            int neighbors = mField.getNeighborsCount(cell);
                            if ((neighbors < liveLeft || neighbors > liveRight) && cell.getState())
                                list.add(cell);
                            else if (neighbors == liveNeed && !cell.getState())
                                list.add(cell);
                        }
                    }
                }
            });
            threads[i].start();
        }
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException ie) {
                System.err.println(ie.getMessage());
            }
        }
        return list;
    }

    public void setInitialState(int[][][] state) {
        mStateStorage = new StateStorage(mField.setFieldState(state));
    }

    public boolean isEmptyField() {
        return false;
    }

    public void runGame(int n) {
        makeMoves(n);
    }

    public void printState() {
        mField.printField();
    }

    public void makeMoves(int n) {
        for (int i = 0; i < n; i++)
            makeMove();
    }

    public void moveToPrev() {
        if (mStateStorage.getSize() > 0) {
            for (Cell cell : addedCells())
                mField.setCellState(cell, false);
            for (Cell cell : removedCells())
                mField.setCellState(cell, true);
            mStateStorage.toPrevState();
            return;
        }
    }

    public List<Cell> prevRemovedCells() {
        List<Cell> cells = mStateStorage.getPrevRemove();
        return cells;
    }

    public List<Cell> prevAddedCells() {
        List<Cell> cells = mStateStorage.getPrevAdd();
        return cells;
    }

    public int getStatesCount() {
        return mStateStorage.getSize();
    }
}
